<?php

/**
 * Emulates a PEAR database connection which is what the Auth_OpenID
 * library expects to use for its database storage.  This just wraps
 * Moodle and Elgg's database abstraction calls.
 */
class Client_OpenID_DBConnection extends Auth_OpenID_DatabaseConnection {

    function setFetchMode($mode = 0) {
		global $db;
		global $ADODB_FETCH_ASSOC;
		
		if ($mode == 0) {
			$mode = $ADODB_FETCH_ASSOC;
		}
		
		return $db->SetFetchMode($mode);
    }

    function autoCommit($mode) {
        // Not implemented.
    }

    function query($sql, $params = array()) {
	    global $db;
	    $mode = $this->setFetchMode();
        $result = $db->Execute($sql, $params);
        $this->setFetchMode($mode);
        return $result;
    }

    function getOne($sql, $params = array()) {
        global $db;
	    $mode = $this->setFetchMode();
        $result = $db->GetOne($sql, $params);
        $this->setFetchMode($mode);
        return $result;
    }

    function getRow($sql, $params = array()) {
        global $db;
	    $mode = $this->setFetchMode();
        $result = $db->GetRow($sql, $params);
        $this->setFetchMode($mode);
        return $result;
    }

    function getAll($sql, $params = array()) {
        global $db;
	    $mode = $this->setFetchMode();
        $result = $db->GetAll($sql, $params);
        $this->setFetchMode($mode);
        return $result;
	}
}


class Client_OpenID_MySQLStore_Moodle extends Auth_OpenID_MySQLStore {
	
	function Client_OpenID_MySQLStore_Moodle() {
		global $CFG;
		$prefix = $CFG->prefix;
		$this->nosync_table_name = $prefix."openid_client_nosync";
	    $this->code_table_name = $prefix."openid_client_code";
	    $conn = new Client_OpenID_DBConnection();
        parent::Auth_OpenID_MySQLStore($conn,
                                       $prefix.'openid_client_consumer_settings',
                                       $prefix.'openid_client_consumer_associations',
                                       $prefix.'openid_client_consumer_nonces');
	    $this->createTables();
    }	
	
    function createTables() {
	    parent::createTables();  
	    $this->create_code_table();
	    $this->create_nosync_table();
   	}
   	
   	function _fixSQL() {
	   	parent::_fixSQL();
	   	$reps = array( array(
	                    'value' => $this->nosync_table_name,
	                    'keys' => array('nosync_table',
	                                    'add_nosync',
	                                    'get_nosync',
	                                    'remove_nosync')
	                    ),
	                    array(
	                    'value' => $this->code_table_name,
	                    'keys' => array('code_table',
	                                    'add_code',
	                                    'get_code',
	                                    'remove_codes')
	                    ));
		foreach ($reps as $replacements) {                  
			foreach ($replacements['keys'] as $key) {
				$this->sql[$key] = sprintf($this->sql[$key],$replacements['value']);
			}
		}
	}
   	
	/**
	 * Moodle does not have a table that can be used to store unforgeable
	 * form and email codes, so this OpenID Store instance creates one.
	 */   	
   	
   	function create_code_table() {   	
	   	if (!$this->tableExists($this->code_table_name)) {
            $r = $this->connection->query($this->sql['code_table']);
            return $this->resultToBool($r);
        }
        return true;
   	}
   	
   	function getCode($code_value) {   	
	   	return $this->connection->getRow($this->sql['get_code'],
                                        array($code_value));
	}
	
	function addCode($code) {
		$code_array = array($code->code,$code->email,$code->username,$code->owner,$code->added);
	   	return $this->connection->query($this->sql['add_code'],
                                        $code_array);
	}
	
	function removeCodes($ts) {
		// remove all codes older than time $ts
	   	return $this->connection->query($this->sql['remove_codes'],
                                        array($ts));
	}
	
/**
 * If the data provided by an OpenID server differs from the data
 * stored on the client site, users may be asked if they want to 
 * update their client data each time they log on to the client.
 * The nosync table lists the idents of users who have asked to turn
 * this question off.
 */   	
   	
   	function create_nosync_table() {   	
	   	if (!$this->tableExists($this->nosync_table_name)) {
            $r = $this->connection->query($this->sql['nosync_table']);
            return $this->resultToBool($r);
        }
        return true;
   	}
   	
   	function getNoSyncStatus($ident) {   	
	   	$r = $this->connection->getOne($this->sql['get_nosync'],
                                        array($ident));
		return $this->resultToBool($r);
	}
	
	function addNoSyncStatus($ident) {
	   	return $this->connection->query($this->sql['add_nosync'],
                                        array($ident));
	}
	
	function removeNoSyncStatus($ident) {
	   	return $this->connection->query($this->sql['remove_nosync'],
                                        array($ident));
	}
	
	/**
     * Returns true if the specified value is considered an error
     * value.  Values returned from database calls will be passed to
     * this function to make decisions.
     */
    function isError($value)
    {
        return $value === false;
    }

    /**
     * This function is responsible for encoding binary data to make
     * it safe for use in SQL.
     */
    function blobEncode($blob)
    {
        return $blob;
    }

    /**
     * Given encoded binary data, this function is responsible for
     * decoding it from its encoded representation.  Some backends
     * will not return encoded data, like this one, so no conversion
     * is necessary.
     */
    function blobDecode($blob)
    {
        return $blob;
    }
	
	/**
     * This is called by the SQLStore constructor and gives the
     * implementor a place to specify SQL that is used to store and
     * retrieve specific data.  Here we call the parent class's setSQL
     * to get the basic SQL data for a MySQL store and overwrite a 
     * couple of pieces of SQL code that use the unsupported "!"
     * placeholder in the default MySQL store. We also add the SQL
     * to support the nosync table used by the OpenID client to
     * support OpenID client/server data syncronisation.
     */
	
	function setSQL()
    {
        parent::setSQL();
            
        $this->sql['code_table'] =
            "CREATE TABLE %s (
			  `id` int(10) unsigned NOT NULL auto_increment,
			  `username` varchar(255) NOT NULL default '',
			  `email` varchar(255) NOT NULL default '',
			  `code` varchar(255) NOT NULL default '',
			  `owner` int(10) unsigned NOT NULL default '0',
			  `added` int(10) NOT NULL default '0',
			  PRIMARY KEY  (`id`),
			  KEY `code` (`code`),
			  KEY `added` (`added`)
			) TYPE=MyISAM COMMENT='Confirmation codes'";
			
		$this->sql['create_auth'] =
            "INSERT INTO %s VALUES ('auth_key', ?)";
            
        $this->sql['set_assoc'] =
            "REPLACE INTO %s VALUES (?, ?, ?, ?, ?, ?)";
            
        $this->sql['nosync_table'] =
            "CREATE TABLE %s (ident INT(10) UNIQUE PRIMARY KEY) TYPE=InnoDB";
            
		$this->sql['get_nosync'] =
			"SELECT ident FROM %s WHERE ident = ?";
			
		$this->sql['add_nosync'] =
            "INSERT INTO %s (ident) VALUES (?)";
            
		$this->sql['remove_nosync'] =
            "DELETE FROM %s WHERE ident = ?";
            
		$this->sql['get_code'] =
			"SELECT * FROM %s WHERE code = ?";
			
		$this->sql['add_code'] =
            "INSERT INTO %s (code,email,username,owner,added) VALUES (?,?,?,?,?)";
            
		$this->sql['remove_codes'] =
            "DELETE FROM %s WHERE added > ?";

    }
}	

?>